﻿/**************************************************************
 *
 * 唯一标识：71f12da3-ca29-4a55-b8a6-e3ed6f4c164d
 * 命名空间：Sgr.Data
 * 创建时间：2024/6/17 21:08:00
 * 机器名称：DESKTOP-HJ4OAG9
 * 创建者：CocoYuan
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Microsoft.Extensions.Logging;
using Sgr.Exceptions;

namespace Sgr.Data
{
    /// <summary>
    /// 默认数据库连接字符串管理器，负责管理和提供数据库连接字符串配置信息（无锁读取实现）
    /// - 读取路径无锁：通过引用快照读取字典（Volatile.Read）
    /// - Reload 时构建新字典并使用 Interlocked.Exchange 原子替换引用
    /// </summary>
    public class DefaultDataConnectionStringManager : IDataConnectionStringManager, IDisposable
    {
        /// <summary>
        /// 数据源字典，存储所有的数据库连接配置
        /// Key: 数据源名称（不区分大小写）
        /// Value: 数据源配置信息
        /// </summary>
        private Dictionary<string, DataConnectionString>? _databaseSources;

        /// <summary>
        /// 数据源提供者集合
        /// 用于加载数据库连接字符串配置
        /// </summary>
        private readonly IEnumerable<IDataConnectionStringProvider> _dataConnectionStringProviders;

        /// <summary>
        /// 是否已释放资源
        /// </summary>
        private volatile bool _disposed;

        /// <summary>
        /// 日志类
        /// </summary>
        private readonly ILogger<DefaultDataConnectionStringManager> _logger;

        /// <summary>
        /// 初始化默认数据库连接字符串管理器
        /// </summary>
        /// <param name="dataConnectionStringProviders">数据源提供者集合</param>
        /// <param name="logger">日志类</param>
        /// <exception cref="ArgumentNullException">当数据源提供者集合为空时抛出</exception>
        public DefaultDataConnectionStringManager(
            IEnumerable<IDataConnectionStringProvider> dataConnectionStringProviders,
            ILogger<DefaultDataConnectionStringManager> logger)
        {
            _dataConnectionStringProviders = dataConnectionStringProviders ??
                throw new ArgumentNullException(nameof(dataConnectionStringProviders));

            _logger = logger ?? throw new ArgumentNullException(nameof(logger));

            _databaseSources = new Dictionary<string, DataConnectionString>(StringComparer.OrdinalIgnoreCase);

            // 预加载一次（保持与旧实现行为一致）
            Reload();
        }

        /// <summary>
        /// 重新加载所有数据源配置
        /// </summary>
        public void Reload()
        {
            ThrowIfDisposed();

            var newDict = new Dictionary<string, DataConnectionString>(StringComparer.OrdinalIgnoreCase);

            int loaded = 0;
            int skipped = 0;
            int providerErrors = 0;

            foreach (var provider in _dataConnectionStringProviders)
            {
                try
                {
                    var sources = provider.LoadDataConnectionStrings();
                    if (sources == null)
                        continue;

                    foreach (var source in sources)
                    {
                        if (source == null)
                        {
                            skipped++;
                            continue;
                        }

                        if (string.IsNullOrWhiteSpace(source.Name))
                        {
                            skipped++;
                            _logger.LogWarning("跳过未命名的数据源定义（来自提供者 {Provider}）", provider.GetType().FullName);
                            continue;
                        }

                        newDict[source.Name] = source;
                        loaded++;
                    }
                }
                catch (Exception ex)
                {
                    providerErrors++;
                    _logger.LogError(ex, "从提供者 {Provider} 加载配置失败，已跳过该提供者。", provider.GetType().FullName);
                }
            }

            // 原子替换引用，读者随后看到的是完整快照（旧或新）
            Interlocked.Exchange(ref _databaseSources, newDict);

            _logger.LogInformation("Reload 数据源完成：已加载 {Loaded} 条，跳过 {Skipped} 条，提供者错误 {ProviderErrors} 个。", loaded, skipped, providerErrors);
        }

        /// <summary>
        /// 获取指定名称的数据源配置
        /// </summary>
        /// <param name="dataConnectionStringName">数据源名称</param>
        /// <returns>数据源配置信息</returns>
        public DataConnectionString GetDataConnectionString(string dataConnectionStringName)
        {
            var snapshot = Volatile.Read(ref _databaseSources) ?? throw new ObjectDisposedException(GetType().Name);
            string key = string.IsNullOrEmpty(dataConnectionStringName)
                ? Constant.DEFAULT_DATABASE_SOURCE_NAME : dataConnectionStringName;

            if (!snapshot.TryGetValue(key, out var source) || source == null)
            {
                _logger.LogWarning("未找到数据源配置：{Name}", key);
                throw new BusinessException($"未设置[{key}]的数据连接字符串");
            }

            return source;
        }

        /// <summary>
        /// 尝试获取指定名称的数据源配置
        /// </summary>
        /// <param name="dataConnectionStringName">数据源名称</param>
        /// <param name="dataConnectionString">输出的数据源配置</param>
        /// <returns>是否成功获取数据源配置</returns>
        /// <exception cref="ObjectDisposedException">当对象已释放时抛出</exception>
        public bool TryGetDataConnectionString(
            string dataConnectionStringName,
            out DataConnectionString? dataConnectionString)
        {
            var snapshot = Volatile.Read(ref _databaseSources);
            if (snapshot == null)
            {
                // 与 GetDataConnectionString 语义一致：当管理器已释放，抛出 ObjectDisposedException
                dataConnectionString = null;
                throw new ObjectDisposedException(GetType().Name);
            }

            string key = string.IsNullOrEmpty(dataConnectionStringName) ? Constant.DEFAULT_DATABASE_SOURCE_NAME : dataConnectionStringName;
            return snapshot.TryGetValue(key, out dataConnectionString);
        }

        /// <summary>
        /// 检查对象是否已释放
        /// </summary>
        /// <exception cref="ObjectDisposedException">当对象已释放时抛出</exception>
        protected void ThrowIfDisposed()
        {
            ObjectDisposedException.ThrowIf(_disposed, GetType().Name);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否正在释放托管资源</param>
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;

            if (disposing)
            {
                // 标记已释放并原子置 null 引用以释放资源与阻止后续读取
                _disposed = true;
                Interlocked.Exchange(ref _databaseSources, null);
            }

            _disposed = true;
        }
    }
}